#include "lp_global_vars.h"
double latency[1500000];
int last = -1;

void traveres_for_gaps() {
	int i;
	int last_gapless_delivery;
	if (lp_interval_performance->last_gapless_delivered == -1)
		last_gapless_delivery = 0;
	else
		last_gapless_delivery = lp_interval_performance->last_gapless_delivered;
	assert(lp_interval_performance->last_gapless_delivered <= lp_interval_performance->last_delivered);
	for (i = last_gapless_delivery; i <= lp_interval_performance->last_delivered; i++) {
		if (p_msgs[i % INTERNAL_BUFFER_SIZE].delivered == 'y') {
			lp_interval_performance->last_gapless_delivered = i;
			assert(lp_interval_performance->last_gapless_delivered <= lp_interval_performance->last_delivered);
		} else {
			assert(lp_interval_performance->last_gapless_delivered <= lp_interval_performance->last_delivered);
			break;
		}
	}
}

void learner_deliver() {
	int i, j;
	bool flag;
	int status = 0;
	int gap_length = 0;
	gap g;

	for (i = lowestDelivered + 1; i < lp_interval_performance->highest_deliverable_inst_id + 1; i++) {
		if (data[(i % INTERNAL_BUFFER_SIZE)].received) {
			if (!data[(i % INTERNAL_BUFFER_SIZE)].deliver) {
				if (data[(i % INTERNAL_BUFFER_SIZE)].packet.pack_id == (i + 1)) {
					flag = true;
					double lat = getMiliseconds() - data[(i % INTERNAL_BUFFER_SIZE)].packet.msgs[0].microseconds;
					lp_interval_performance->interval_latency += lat;

					if (lp_next_performance_index > DISCARD && lp_next_performance_index < STOPPING_CONDITION - DISCARD) {
						latency[lp_interval_performance->latency_counter] = lat;
						lp_interval_performance->latency_counter++;
					}

					if (is_proposer == 1) {
						int location_in_proposer = data[(i % INTERNAL_BUFFER_SIZE)].packet.msgs[0].location_in_proposer;
						p_msgs[location_in_proposer % INTERNAL_BUFFER_SIZE].delivered = 'y';
						if (location_in_proposer > lp_interval_performance->last_delivered)
							lp_interval_performance->last_delivered = location_in_proposer;
						traveres_for_gaps();
					}

					//					data[(i % INTERNAL_BUFFER_SIZE)].dec = data[(i % INTERNAL_BUFFER_SIZE)].packet.pack_id;
					//					data[(i % INTERNAL_BUFFER_SIZE)].deliver = true;
					//					data[(i % INTERNAL_BUFFER_SIZE)].received = false;
					//					data[(i % INTERNAL_BUFFER_SIZE)].deliver = false;
					//					data[(i % INTERNAL_BUFFER_SIZE)].packet.msgs[0].msg_id = 0;
					//					data[(i % INTERNAL_BUFFER_SIZE)].packet.pack_id = 0;

					lp_interval_performance->total_num_packets_delivered++;
					lp_interval_performance->interval_num_packets_delivered++;
				}
			}
		} else {

			g.start = i;
			g.end = i;
			printf("missing	%d >? %d\n", g.start, last);
			gap_length = 1;
			if (g.start > last) {
				for (j = i + 1; j < lp_interval_performance->highest_deliverable_inst_id; j++) {
					if (gap_length < MAX_GAP_LENGTH) {
						if (data[(j % INTERNAL_BUFFER_SIZE)].received)
							break;
						else {
							g.end = j;
							gap_length++;
						}
					}
				}
				//				if (last != g.end) {
				last = g.end;
				lp_interval_performance->interval_num_packets_lost++;
				printf("SENDING: PROCESS: %d,  HD:%d, %d, gap:start:%d, gapEnd:%d socket: %d\n", get_config_info()->learner_id, lp_interval_performance->highest_deliverable_inst_id, lp_interval_performance->total_num_packets_delivered, g.start, g.end, get_config_info()->learner_infos[get_config_info()->learner_id].learner_to_acceptor_sid);
				status = send(get_config_info()->learner_infos[get_config_info()->learner_id].learner_to_acceptor_sid, &g, sizeof(gap), MSG_WAITALL);
				assert(status == sizeof(gap));
				//				}
			}
			break;
		}

	}
	lowestDelivered = i - 1;
}
//------------------------------------------------------------------------------
void recv_new_packet(int sd, short event, void * arg) {
	UNUSED_ARG(event);
	UNUSED_ARG(arg);
	int status = -1;
	unsigned int len = sizeof(struct sockaddr);
	status = recvfrom(sd, new_data, sizeof(inst), MSG_WAITALL, (struct sockaddr *) ipmAddr, &len);
	assert(status == sizeof(inst));
	//	printf("%d\n", new_data->instance_id);
	lp_interval_performance->interval_bytes_recvied += status;

	if (lp_interval_performance->highest_deliverable_inst_id != new_data->highestDeliverable) {
		lp_interval_performance->highest_deliverable_inst_id = new_data->highestDeliverable;
		learner_deliver();
	}
	if (data[(new_data->instance_id % INTERNAL_BUFFER_SIZE)].packet.pack_id == new_data->packet.pack_id) {
		lp_interval_performance->interval_num_redundant_packets_received++;
		lp_interval_performance->total_num_redundant_packets_received++;
	} else {
		memcpy(&data[(new_data->instance_id % INTERNAL_BUFFER_SIZE)], new_data, sizeof(inst));
		int index = (new_data->instance_id % INTERNAL_BUFFER_SIZE);
		data[index].received = true;
		data[index].deliver = false;
		data[index].highestDeliverable = -1;
	}
}
//------------------------------------------------------------------------------
void recv_lost_packet(int sd, short event, void *arg) {
	int status;
	inst temp;
	status = recv(sd, &temp, sizeof(inst), MSG_WAITALL);
	assert(status == sizeof(inst));
	lp_interval_performance->interval_bytes_recvied += status;

	if ((temp.instance_id == (temp.packet.pack_id - 1))) {
		memcpy(&data[(temp.instance_id % INTERNAL_BUFFER_SIZE)], &temp, sizeof(inst));
		data[(temp.instance_id % INTERNAL_BUFFER_SIZE)].received = true;
		data[(temp.instance_id % INTERNAL_BUFFER_SIZE)].deliver = false;
		data[(temp.instance_id % INTERNAL_BUFFER_SIZE)].highestDeliverable = -1;
	} else {
		printf("%d is OVERWRITTEN already\n", temp.instance_id);
		lp_interval_performance->interval_num_overwritten_insts++;
		lp_interval_performance->total_num_overwritten_insts++;
		//looks like a hack!
		data[((temp.instance_id) % INTERNAL_BUFFER_SIZE)].received = true;
		data[((temp.instance_id) % INTERNAL_BUFFER_SIZE)].deliver = true;
	}
}

//------------------------------------------------------------------------------

